﻿using System;
using System.Collections.Generic;
using STT.Debug;

namespace STT.Parser {

    public class UdtFormatGeneric {

        public UdtFormatGeneric(ModuleSymbols _mod, SymUdt _sym_udt) {
            mod            = _mod;
            sym_udt        = _sym_udt;
            udt_dependency = false;
            list_member    = new List<SymbolGenericFormat>();
            BLOCK_TYPE block_type;
            if (sym_udt.UdtKind == 0U) {
                if (IsGlobalStructOfBitField(sym_udt))
                    block_type = BLOCK_TYPE.GLOBAL_BITS_STRUCT;
                else
                    block_type = BLOCK_TYPE.GLOBAL_STRUCT;
            }
            else if (sym_udt.UdtKind == 2U) {
                block_type = BLOCK_TYPE.GLOBAL_UNION;
            }
            else {
                block_type = BLOCK_TYPE.GLOBAL_CLASS;
            }

            AddStartBlock(block_type, null, 0U, sym_udt.ChildrenCount, sym_udt.LengthOfType);
            uint num;
            for (num = 0U; num < sym_udt.ChildrenCount; num += 1U) {
                var     symData = (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId[(int) (UIntPtr) num]);
                var     symByTypeId = mod.list_symbol.GetSymByTypeId(symData.TypeId_next);
                SymData symData2;
                Symbol  symbol;
                if (num + 1U < sym_udt.ChildrenCount) {
                    symData2 =
                        (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId[(int) (UIntPtr) (num + 1U)]);
                    symbol = mod.list_symbol.GetSymByTypeId(symData2.TypeId_next);
                }
                else {
                    symData2 = null;
                    symbol   = null;
                }

                if (symData2 != null) {
                    for (;;) {
                        switch (GetCurrentBlockType()) {
                        case BLOCK_TYPE.GLOBAL_STRUCT: {
                            var sizeLocalUnionMember = GetSizeLocalUnionMember(mod, sym_udt, num);
                            if (sizeLocalUnionMember > 0UL) {
                                AddStartBlock(BLOCK_TYPE.LOCAL_UNION, GetLastStartBlock(), symData.TypeOffset, 0U,
                                              sizeLocalUnionMember);
                                continue;
                            }

                            goto IL_257;
                        }
                        case BLOCK_TYPE.GLOBAL_UNION:
                        case BLOCK_TYPE.LOCAL_UNION:
                            if (symData.TypeOffset == symData2.TypeOffset && symByTypeId.AreBaseTypesEqual(symbol) &&
                                symData.BitField                          && symData2.BitField) {
                                if (AreTwoBitFieldSuccessiveStructures(mod, sym_udt, num,
                                                                       GetLastStartBlock().offset_max)) {
                                    AddStartBlock(BLOCK_TYPE.LOCAL_STRUCT, GetLastStartBlock(), symData.TypeOffset, 0U,
                                                  GetLastStartBlock().size_max);
                                    continue;
                                }

                                goto IL_319;
                            }
                            else if (symData.TypeOffset                        < symData2.TypeOffset              &&
                                     symData.TypeOffset                        == GetLastStartBlock().offset_base &&
                                     symData2.TypeOffset                       > GetLastStartBlock().offset_base  &&
                                     symData2.TypeOffset + symbol.LengthOfType <= GetLastStartBlock().offset_max) {
                                AddStartBlock(BLOCK_TYPE.LOCAL_STRUCT, GetLastStartBlock(), symData.TypeOffset, 0U,
                                              GetLastStartBlock().size_max);
                                continue;
                            }

                            break;

                        case BLOCK_TYPE.LOCAL_STRUCT: {
                            if (symData.TypeOffset <= GetLastStartBlock().offset_base) goto IL_1AD;
                            var sizeLocalUnionMember = GetSizeLocalUnionMember(mod, sym_udt, num);
                            if (sizeLocalUnionMember > 0UL && symData.TypeOffset + sizeLocalUnionMember <=
                                GetLastStartBlock().offset_max) {
                                AddStartBlock(BLOCK_TYPE.LOCAL_UNION, GetLastStartBlock(), symData.TypeOffset, 0U,
                                              sizeLocalUnionMember);
                                continue;
                            }

                            goto IL_1AD;
                        }
                        }

                        break;
                    }

                    goto IL_3AC;
                    IL_1AD:
                    if (symData.TypeOffset == symData2.TypeOffset && symData.BitField && symData2.BitField &&
                        symByTypeId.AreBaseTypesEqual(symbol)     &&
                        symData2.TypeOffset + symbol.LengthOfType <= GetLastStartBlock().offset_max) {
                        AddStartBlock(BLOCK_TYPE.LOCAL_BITS_STRUCT, GetLastStartBlock(), symData.TypeOffset, 0U,
                                      symByTypeId.LengthOfType);
                    }

                    goto IL_3AC;
                    IL_257:
                    if (symData.TypeOffset == symData2.TypeOffset && symByTypeId.AreBaseTypesEqual(symbol) &&
                        symData.BitField                          && symData2.BitField)
                        AddStartBlock(BLOCK_TYPE.LOCAL_BITS_STRUCT, GetLastStartBlock(), symData.TypeOffset, 0U,
                                      symByTypeId.LengthOfType);
                    goto IL_3AC;
                    IL_319:
                    AddStartBlock(BLOCK_TYPE.LOCAL_BITS_STRUCT, GetLastStartBlock(), symData.TypeOffset, 0U,
                                  symByTypeId.LengthOfType);
                }

                IL_3AC:
                if (AddDataMember(symData).sym_udt_dependency != null) udt_dependency = true;
                switch (GetCurrentBlockType()) {
                case BLOCK_TYPE.LOCAL_STRUCT:
                    GetLastStartBlock().number_of_member += 1U;
                    break;

                case BLOCK_TYPE.LOCAL_BITS_STRUCT:
                case BLOCK_TYPE.LOCAL_UNION:
                    GetLastStartBlock().number_of_member += 1U;
                    break;
                }

                if (symData2 != null)
                    for (;;) {
                        switch (GetCurrentBlockType()) {
                        case BLOCK_TYPE.LOCAL_STRUCT:
                            if (symData2.TypeOffset >= GetBeforeLastStartBlock().offset_max ||
                                symData2.TypeOffset <= symData.TypeOffset) {
                                AddEndBlock();
                                if (GetCurrentBlockType() == BLOCK_TYPE.LOCAL_UNION) {
                                    GetLastStartBlock().number_of_member += 1U;
                                    continue;
                                }
                            }

                            break;

                        case BLOCK_TYPE.LOCAL_BITS_STRUCT:
                            if (symData.TypeOffset != symData2.TypeOffset || !symData2.BitField) {
                                AddEndBlock();
                                if (GetCurrentBlockType() == BLOCK_TYPE.LOCAL_UNION ||
                                    GetCurrentBlockType() == BLOCK_TYPE.LOCAL_STRUCT) {
                                    GetLastStartBlock().number_of_member += 1U;
                                    continue;
                                }
                            }

                            break;

                        case BLOCK_TYPE.LOCAL_UNION:
                            if (symData2.TypeOffset >= GetLastStartBlock().offset_max ||
                                symData2.TypeOffset < GetLastStartBlock().offset_base) {
                                var size_max = GetLastStartBlock().size_max;
                                AddEndBlock();
                                if (GetCurrentBlockType() == BLOCK_TYPE.LOCAL_STRUCT) {
                                    GetLastStartBlock().number_of_member += 1U;
                                    continue;
                                }
                            }

                            break;
                        }

                        break;
                    }
                else
                    while (GetCurrentLevel()     > 1U && GetCurrentBlockType() != BLOCK_TYPE.GLOBAL_BITS_STRUCT &&
                           GetCurrentBlockType() != BLOCK_TYPE.GLOBAL_STRUCT &&
                           GetCurrentBlockType() != BLOCK_TYPE.GLOBAL_UNION)
                        if (GetCurrentBlockType() == BLOCK_TYPE.LOCAL_UNION) {
                            AddEndBlock();
                            if (GetCurrentBlockType() == BLOCK_TYPE.LOCAL_STRUCT)
                                GetLastStartBlock().number_of_member += 1U;
                        }
                        else {
                            if (GetCurrentBlockType() != BLOCK_TYPE.LOCAL_STRUCT &&
                                GetCurrentBlockType() != BLOCK_TYPE.LOCAL_BITS_STRUCT) break;
                            AddEndBlock();
                            if (GetCurrentBlockType() == BLOCK_TYPE.LOCAL_UNION)
                                GetLastStartBlock().number_of_member += 1U;
                        }
            }

            AddEndBlock((StartBlock) list_member[0]);
            num = 0U;
            while (num < (ulong) list_member.Count) {
                var symbolGenericFormat = list_member[(int) num];
                if (symbolGenericFormat is EndBlock) {
                    var endBlock = (EndBlock) symbolGenericFormat;
                    if (!endBlock.start_block.IsStruct()) {
                        var v = endBlock.start_block.GetLength() - endBlock.start_block.padding;
                        if (v > 0xfffff) //如果为负数就跳过
                            endBlock.padding = 0;
                        else
                            endBlock.padding = v;
                    }
                       
                }
                else if (symbolGenericFormat.block_parent != null) {
                    if (symbolGenericFormat.block_parent.IsStruct()) {
                        int index;
                        if (symbolGenericFormat is StartBlock)
                            index =
                                list_member.IndexOf(((StartBlock) symbolGenericFormat).end_block, (int) (num + 1U)) + 1;
                        else
                            index = (int) (num + 1U);
                        var symbolGenericFormat2 = list_member[index];
                        if (symbolGenericFormat2 is EndBlock) {
                            var v =
                                symbolGenericFormat.block_parent.GetOffset() +
                                symbolGenericFormat.block_parent.GetLength() -
                                (symbolGenericFormat.GetOffset() + symbolGenericFormat.GetLength());
                            if (v > 0xfffff) //如果为负数就跳过
                                symbolGenericFormat.block_parent.padding = 0;
                            else
                                symbolGenericFormat.block_parent.padding = v;
                        }

                        else {
                            var v =
                                symbolGenericFormat2.GetOffset() -
                                (symbolGenericFormat.GetOffset() + symbolGenericFormat.GetLength());
                            if (v > 0xfffff) //如果为负数就跳过
                                symbolGenericFormat.padding = 0;
                            else
                                symbolGenericFormat.padding = v;
                        }
                            
                    }
                    else if (symbolGenericFormat.block_parent.padding < symbolGenericFormat.GetLength()) {
                        symbolGenericFormat.block_parent.padding = symbolGenericFormat.GetLength();
                    }
                }

                num += 1U;
            }
        }

        // Token: 0x17000012 RID: 18
        // (get) Token: 0x060000BB RID: 187 RVA: 0x00007D58 File Offset: 0x00006D58
        public ModuleSymbols mod { get; }

        // Token: 0x17000013 RID: 19
        // (get) Token: 0x060000BC RID: 188 RVA: 0x00007D60 File Offset: 0x00006D60
        public SymUdt sym_udt { get; }

        // Token: 0x17000014 RID: 20
        // (get) Token: 0x060000BD RID: 189 RVA: 0x00007D68 File Offset: 0x00006D68
        public List<SymbolGenericFormat> list_member { get; }

        // Token: 0x17000015 RID: 21
        // (get) Token: 0x060000BE RID: 190 RVA: 0x00007D70 File Offset: 0x00006D70
        // (set) Token: 0x060000BF RID: 191 RVA: 0x00007D78 File Offset: 0x00006D78
        public bool udt_dependency { get; set; }

        // Token: 0x17000016 RID: 22
        // (get) Token: 0x060000C0 RID: 192 RVA: 0x00007D81 File Offset: 0x00006D81
        // (set) Token: 0x060000C1 RID: 193 RVA: 0x00007D89 File Offset: 0x00006D89
        public uint level_max { get; set; }

        // Token: 0x060000C2 RID: 194 RVA: 0x00007D94 File Offset: 0x00006D94
        private bool IsGlobalStructOfBitField(SymUdt sym_udt) {
            var num = 0U;
            while (num < sym_udt.ChildrenCount) {
                var symData = (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId[(int) (UIntPtr) num++]);
                if (!symData.BitField || symData.TypeOffset != 0U) return false;
            }

            return true;
        }

        // Token: 0x060000C3 RID: 195 RVA: 0x00007DE4 File Offset: 0x00006DE4
        private bool AreTwoBitFieldSuccessiveStructures(ModuleSymbols mod, SymUdt sym_udt, uint IndexChild,
                                                        ulong         offset_max) {
            var symData = (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId[(int) (UIntPtr) IndexChild]);
            if (!symData.BitField) return false;
            var bitPosition = symData.BitPosition;
            var typeOffset  = symData.TypeOffset;
            var symByTypeId = mod.list_symbol.GetSymByTypeId(symData.TypeId_next);
            var num         = IndexChild + 1U;
            while (num < sym_udt.ChildrenCount) {
                var symData2     = (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId[(int) (UIntPtr) num]);
                var symByTypeId2 = mod.list_symbol.GetSymByTypeId(symData2.TypeId_next);
                if (!symByTypeId.AreBaseTypesEqual(symByTypeId2) || !symData2.BitField) break;
                if (typeOffset == symData2.TypeOffset && bitPosition < symData2.BitPosition) {
                    bitPosition =  symData2.BitPosition;
                    num         += 1U;
                }
                else {
                    if (symData2.TypeOffset                             > typeOffset &&
                        symData2.TypeOffset + symByTypeId2.LengthOfType <= offset_max) return true;
                    break;
                }
            }

            return false;
        }

        // Token: 0x060000C4 RID: 196 RVA: 0x00007ED0 File Offset: 0x00006ED0
        private ulong GetSizeLocalUnionMember(ModuleSymbols mod, SymUdt sym_udt, uint IndexChild) {
            var flag    = false;
            var flag2   = false;
            var flag3   = false;
            var num     = 0U;
            var symData = (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId[(int) (UIntPtr) IndexChild]);
            if (!symData.BitField)
                flag2 = true;
            else
                num = symData.BitPosition;
            var typeOffset  = symData.TypeOffset;
            var symByTypeId = mod.list_symbol.GetSymByTypeId(symData.TypeId_next);
            var num2        = symByTypeId.LengthOfType;
            var num3        = num2;
            var flag4       = false;
            var num4        = IndexChild + 1U;
            while (num4 < sym_udt.ChildrenCount) {
                symData = (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId[(int) (UIntPtr) num4]);
                if (symData.TypeOffset < typeOffset) {
                    if (!flag4 && num3 > num2) num2 = num3;
                    break;
                }

                if (!symData.BitField) {
                    flag2 = true;
                    num   = 0U;
                }
                else {
                    if (symData.BitPosition < num) flag3 = true;
                    num = symData.BitPosition;
                }

                if (num == 0U) {
                    symByTypeId = mod.list_symbol.GetSymByTypeId(symData.TypeId_next);
                    if (symData.TypeOffset == typeOffset) {
                        flag = true;
                        if (num3 > num2) num2 = num3;
                        if (num4 + 1U < sym_udt.ChildrenCount) {
                            var symData2 =
                                (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId
                                                                             [(int) (UIntPtr) (num4 + 1U)]);
                            if (symData2.TypeOffset                      > symData.TypeOffset &&
                                symData2.TypeOffset - symData.TypeOffset > symByTypeId.LengthOfType)
                                num3 = symData2.TypeOffset - typeOffset;
                            else
                                num3 = symData.TypeOffset - typeOffset + symByTypeId.LengthOfType;
                        }
                        else {
                            num3 = sym_udt.LengthOfType - typeOffset;
                        }

                        if (num3 > num2) num2 = num3;
                        flag4 = false;
                    }
                    else {
                        if (!flag4 && symData.TypeOffset >= typeOffset + num2) {
                            if (num3 > num2) num2 = num3;
                            flag4 = true;
                        }

                        if (num4 + 1U < sym_udt.ChildrenCount) {
                            var symData2 =
                                (SymData) mod.list_symbol.GetSymByTypeId(sym_udt.ArrayChildId
                                                                             [(int) (UIntPtr) (num4 + 1U)]);
                            if (symData2.TypeOffset                      > symData.TypeOffset &&
                                symData2.TypeOffset - symData.TypeOffset > symByTypeId.LengthOfType)
                                num3 = symData2.TypeOffset - typeOffset;
                            else
                                num3 = symData.TypeOffset - typeOffset + symByTypeId.LengthOfType;
                        }
                        else {
                            num3 = sym_udt.LengthOfType - typeOffset;
                        }
                    }
                }

                num4 += 1U;
            }

            if (flag && (flag2 || flag3)) return num2;
            return 0UL;
        }

        // Token: 0x060000C5 RID: 197 RVA: 0x00008128 File Offset: 0x00007128
        private BLOCK_TYPE GetCurrentBlockType() {
            var lastStartBlock = GetLastStartBlock();
            if (lastStartBlock != null) return lastStartBlock.block_type;
            return BLOCK_TYPE.TYPE_ERROR;
        }

        // Token: 0x060000C6 RID: 198 RVA: 0x00008147 File Offset: 0x00007147
        private StartBlock GetLastStartBlock() {
            if (list_member.Count > 0) return GetLastStartBlock((uint) (list_member.Count - 1));
            return null;
        }

        // Token: 0x060000C7 RID: 199 RVA: 0x0000816C File Offset: 0x0000716C
        private StartBlock GetLastStartBlock(uint index) {
            if (index < (ulong) list_member.Count) {
                var symbolGenericFormat = list_member[(int) index];
                if (symbolGenericFormat is Data_Member || symbolGenericFormat is EndBlock)
                    return symbolGenericFormat.block_parent;
                if (symbolGenericFormat is StartBlock) return (StartBlock) symbolGenericFormat;
            }

            return null;
        }

        // Token: 0x060000C8 RID: 200 RVA: 0x000081C0 File Offset: 0x000071C0
        private uint GetCurrentLevel() {
            if (list_member.Count > 0) {
                var symbolGenericFormat = list_member[list_member.Count - 1];
                return symbolGenericFormat.level;
            }

            return 0U;
        }

        // Token: 0x060000C9 RID: 201 RVA: 0x000081FC File Offset: 0x000071FC
        private void AddStartBlock(BLOCK_TYPE _block_type,       StartBlock _block_parent, uint _offset_base,
                                   uint       _number_of_member, ulong      _sizeof_block) {
            var currentLevel = GetCurrentLevel();
            list_member.Add(new StartBlock(_block_parent, currentLevel + 1U, _block_type, _offset_base, _sizeof_block,
                                           _number_of_member));
        }

        // Token: 0x060000CA RID: 202 RVA: 0x0000822C File Offset: 0x0000722C
        private Data_Member AddDataMember(SymData _sym_data) {
            var data_Member = new Data_Member(GetLastStartBlock(), _sym_data, mod);
            list_member.Add(data_Member);
            return data_Member;
        }

        // Token: 0x060000CB RID: 203 RVA: 0x0000825C File Offset: 0x0000725C
        private uint AddEndBlock() {
            var num = GetCurrentLevel();
            if (num > 0U) {
                num -= 1U;
                list_member.Add(new EndBlock(GetLastStartBlock(), GetBeforeLastStartBlock(), num));
            }

            return num;
        }

        // Token: 0x060000CC RID: 204 RVA: 0x00008296 File Offset: 0x00007296
        private uint AddEndBlock(StartBlock start_block) {
            list_member.Add(new EndBlock(start_block, null, 0U));
            return 0U;
        }

        // Token: 0x060000CD RID: 205 RVA: 0x000082AC File Offset: 0x000072AC
        private StartBlock GetBeforeLastStartBlock() {
            if (list_member.Count > 0) return GetBeforeLastStartBlock((uint) (list_member.Count - 1));
            return null;
        }

        // Token: 0x060000CE RID: 206 RVA: 0x000082D4 File Offset: 0x000072D4
        private StartBlock GetBeforeLastStartBlock(uint index) {
            var lastStartBlock = GetLastStartBlock(index);
            if (lastStartBlock != null) return lastStartBlock.block_parent;
            return null;
        }
    }
}